﻿// Developer Express Code Central Example:
// Multiple selection using checkbox (web style)
// 
// The following example shows how to implement multiple selection in the web style
// (via check boxes). The XtraGrid does not have this functionality built-in. The
// GridCheckMarksSelection class allows you to implement it. End-users can
// select/unselect rows, group rows or select/unselect all rows by clicking on the
// column header. Changing check box value does not start row editing. This example
// is based on the article. See Also:
// 
// You can find sample updates and versions for different programming languages here:
// http://www.devexpress.com/example=E1271

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraGrid.Views.Grid.ViewInfo;
using DevExpress.Utils.Drawing;
using System.Data;


namespace AccessControl.Utility.Grid
{

	public class CheckMarkSelection
	{
        protected GridView _view;
        public ArrayList selection;
        GridColumn column;
        string identityColumn;
        RepositoryItemCheckEdit edit;
        const int CheckboxIndent = 4;

        public bool IsBatchAction { get; set; }
        public bool NoBatchAction { get; set; }

        public struct SelectionChangedEventArgs {
            public bool IsSelected;
            public int RowHandle;
            public SelectionChangedEventArgs(int rowHandle, bool isSelected) {
                RowHandle = rowHandle;
                IsSelected = isSelected;
            }
        }
        public delegate void SelectionChangedEventHandler(object sender, SelectionChangedEventArgs e);

        public event SelectionChangedEventHandler SelectionChanged;

        public CheckMarkSelection() {
            selection = new ArrayList();
        }
        public CheckMarkSelection(GridView view, string _identityColumn)
            : this(view) {

            identityColumn = _identityColumn;
        }

        public CheckMarkSelection(GridView view)
            : this() {
            View = view;
        }
        public GridView View {
            get { return _view; }
            set {
                if (_view != value) {
                    Detach();
                    Attach(value);
                }
            }
        }
        public GridColumn CheckMarkColumn { get { return column; } }

        public int SelectedCount { get { return selection.Count; } }
        public object GetSelectedRow(int index) {
            if (selection.Count >= index + 1)
                return selection[index];
            else return null;
        }
        public int GetSelectedIndex(object row) {
            return selection.IndexOf(row);
        }
        public void ClearSelection() {
            selection.Clear();
            Invalidate();
            if (SelectionChanged != null) SelectionChanged(View, new SelectionChangedEventArgs(-1, false));
        }
        public void SelectAll() {
            selection.Clear();
            // fast (won't work if the grid is filtered)
            //if(_view.DataSource is ICollection)
            //	selection.AddRange(((ICollection)_view.DataSource));
            //else
            // slow:
            if (!NoBatchAction) IsBatchAction = true;
            for (var i = 0; i < _view.DataRowCount; i++) {
                if (_view.GetRow(i).GetType()!=typeof(DataRowView) || ((DataRowView)_view.GetRow(i)).Row.RowState==DataRowState.Unchanged){
                    if (NoBatchAction) SelectRow(i,true);
                    else selection.Add(_view.GetRow(i));
                }
                    
            }
            if (!NoBatchAction) IsBatchAction = false;
            Invalidate();
            if (SelectionChanged != null)
            {
                if (NoBatchAction)
                    SelectionChanged(View, new SelectionChangedEventArgs(0, IsRowSelected(0)));
                else
                    SelectionChanged(View, new SelectionChangedEventArgs(-1, true));
            }
        }
        public void SelectGroup(int rowHandle, bool select) {
            if (IsGroupRowSelected(rowHandle) && select) return;
            IsBatchAction = true;
            for (int i = 0; i < _view.GetChildRowCount(rowHandle); i++) {
                int childRowHandle = _view.GetChildRowHandle(rowHandle, i);
                if (_view.IsGroupRow(childRowHandle))
                    SelectGroup(childRowHandle, select);
                else
                    SelectRow(childRowHandle, select, false);
            }
            IsBatchAction = false;
            if (SelectionChanged != null) SelectionChanged(View, new SelectionChangedEventArgs(0, IsRowSelected(0)));
            Invalidate();
        }
        public void SelectRow(int rowHandle, bool select) {
            SelectRow(rowHandle, select, true);
        }
        public void InvertRowSelection(int rowHandle) {
            if (View.IsDataRow(rowHandle)) {
                SelectRow(rowHandle, !IsRowSelected(rowHandle));
            }
            if (View.IsGroupRow(rowHandle)) {
                SelectGroup(rowHandle, !IsGroupRowSelected(rowHandle));
            }
        }
        public bool IsGroupRowSelected(int rowHandle) {
            for (int i = 0; i < _view.GetChildRowCount(rowHandle); i++) {
                int row = _view.GetChildRowHandle(rowHandle, i);
                if (_view.IsGroupRow(row)) {
                    if (!IsGroupRowSelected(row)) return false;
                } else
                    if (!IsRowSelected(row)) return false;
            }
            return true;
        }
        public bool IsRowSelected(int rowHandle) {
            if (_view.IsGroupRow(rowHandle))
                return IsGroupRowSelected(rowHandle);

            object row = _view.GetRow(rowHandle);
            return GetSelectedIndex(row) != -1;
        }

        protected virtual void Attach(GridView view) {
            view.BestFitMaxRowCount=100;


            if (view == null) return;
            selection.Clear();
            this._view = view;
            view.BeginUpdate();
            try {
                edit = view.GridControl.RepositoryItems.Add("CheckEdit") as RepositoryItemCheckEdit;

                column = view.Columns.Add();
                column.Name = "colCheckbox";
                column.OptionsColumn.AllowSort = DevExpress.Utils.DefaultBoolean.False;
                column.Visible = true;
                column.VisibleIndex = int.MaxValue;
                column.FieldName = "CheckMarkSelection";
                column.Caption = "انتخاب";
                column.OptionsColumn.ShowCaption = false;
                column.OptionsColumn.AllowEdit = false;
                column.OptionsColumn.AllowSize = false;
                column.UnboundType = DevExpress.Data.UnboundColumnType.Boolean;
                column.MaxWidth = 25; //GetCheckBoxWidth();
                column.ColumnEdit = edit;

                view.Click += new EventHandler(View_Click);
                //view.DataSourceChanged += new EventHandler(View_DatasourceChanged);
                view.CustomDrawColumnHeader += new ColumnHeaderCustomDrawEventHandler(View_CustomDrawColumnHeader);
                view.CustomDrawGroupRow += new RowObjectCustomDrawEventHandler(View_CustomDrawGroupRow);
                view.CustomUnboundColumnData += new CustomColumnDataEventHandler(view_CustomUnboundColumnData);
                view.SelectionChanged += new DevExpress.Data.SelectionChangedEventHandler(View_SelectionChanged);
                view.RowCountChanged += new EventHandler(view_RowCountChanged);

                view.KeyDown += new KeyEventHandler(view_KeyDown);
                view.RowStyle += new RowStyleEventHandler(view_RowStyle);
                view.KeyPress += new KeyPressEventHandler(View_Keypress);

                view.OptionsSelection.MultiSelect = true;
                view.OptionsSelection.MultiSelectMode = GridMultiSelectMode.RowSelect;
                view.OptionsSelection.EnableAppearanceFocusedCell = false;
            } finally {
                view.EndUpdate();
            }
        }


        string typedString;
        DateTime lastTypeDate;
        private void View_Keypress(object sender, KeyPressEventArgs e) {

            if (!View.OptionsBehavior.Editable) {
                if ((DateTime.Now - lastTypeDate).TotalMilliseconds > 1000) typedString = "";

                lastTypeDate = DateTime.Now;
                typedString += e.KeyChar;

                View.UnselectRow(View.FocusedRowHandle);
                View.FocusedRowHandle = FindRow(0, true, typedString);
                View.SelectRow(View.FocusedRowHandle);
            }
        }
        int FindRow(int startRowHandle, bool isForward, string SearchText) {
            startRowHandle = Math.Max(0, startRowHandle);
            try {
                int delta = isForward ? 1 : -1;
                int n = startRowHandle;
                while (View.IsValidRowHandle(n)) {
                    n += delta;

                    string cellText = View.GetRowCellDisplayText(n, View.FocusedColumn);
                    if (cellText.StartsWith(SearchText)) return n;

                }
            } catch { }
            return GridControl.InvalidRowHandle;
        }

        protected virtual void Detach() {
            if (_view == null) return;
            if (column != null)
                column.Dispose();
            if (edit != null) {
                _view.GridControl.RepositoryItems.Remove(edit);
                edit.Dispose();
            }

            _view.Click -= new EventHandler(View_Click);
            _view.CustomDrawColumnHeader -= new ColumnHeaderCustomDrawEventHandler(View_CustomDrawColumnHeader);
            _view.CustomDrawGroupRow -= new RowObjectCustomDrawEventHandler(View_CustomDrawGroupRow);
            _view.CustomUnboundColumnData -= new CustomColumnDataEventHandler(view_CustomUnboundColumnData);
            _view.SelectionChanged -= new DevExpress.Data.SelectionChangedEventHandler(View_SelectionChanged);
            _view.KeyDown -= new KeyEventHandler(view_KeyDown);
            _view.RowStyle -= new RowStyleEventHandler(view_RowStyle);
            _view.RowCountChanged -= new EventHandler(view_RowCountChanged);

            _view = null;
        }
        protected int GetCheckBoxWidth() {
            DevExpress.XtraEditors.ViewInfo.CheckEditViewInfo info = edit.CreateViewInfo() as DevExpress.XtraEditors.ViewInfo.CheckEditViewInfo;
            int width = 0;
            GraphicsInfo.Default.AddGraphics(null);
            try {
                width = info.CalcBestFit(GraphicsInfo.Default.Graphics).Width;
            } finally {
                GraphicsInfo.Default.ReleaseGraphics();
            }
            return width + CheckboxIndent * 2;
        }
        protected void DrawCheckBox(Graphics g, Rectangle r, bool Checked) {
            DevExpress.XtraEditors.ViewInfo.CheckEditViewInfo info;
            DevExpress.XtraEditors.Drawing.CheckEditPainter painter;
            DevExpress.XtraEditors.Drawing.ControlGraphicsInfoArgs args;
            info = edit.CreateViewInfo() as DevExpress.XtraEditors.ViewInfo.CheckEditViewInfo;
            painter = edit.CreatePainter() as DevExpress.XtraEditors.Drawing.CheckEditPainter;
            info.EditValue = Checked;
            info.Bounds = r;
            info.CalcViewInfo(g);
            args = new DevExpress.XtraEditors.Drawing.ControlGraphicsInfoArgs(info, new DevExpress.Utils.Drawing.GraphicsCache(g), r);
            painter.Draw(args);
            args.Cache.Dispose();
        }
        void Invalidate() {
            _view.BeginUpdate();
            _view.EndUpdate();
        }
        void SelectRow(int rowHandle, bool select, bool invalidate) {
            if (IsRowSelected(rowHandle) == select) return;
            object row = _view.GetRow(rowHandle);
            if (select)
            {
                if (row.GetType()!=typeof(DataRowView) || ((DataRowView)row).Row.RowState == DataRowState.Unchanged) 
                    selection.Add(row);
            }
            else
                selection.Remove(row);

            if (invalidate) {
                Invalidate();
            }
            if (SelectionChanged != null) SelectionChanged(View, new SelectionChangedEventArgs(rowHandle, select));
        }
        void view_CustomUnboundColumnData(object sender, CustomColumnDataEventArgs e) {
            if (e.Column == CheckMarkColumn) {
                if (e.IsGetData)
                    e.Value = IsRowSelected(e.RowHandle);
                else
                    SelectRow(e.RowHandle, (bool)e.Value);
            }
        }
        void view_KeyDown(object sender, KeyEventArgs e) {
            if ((!View.IsEditing || View.FocusedColumn == column) && e.KeyCode == Keys.Space)
                InvertRowSelection(View.FocusedRowHandle);
        }
        void View_Click(object sender, EventArgs e) {
            GridHitInfo info;
            Point pt = _view.GridControl.PointToClient(Control.MousePosition);
            info = _view.CalcHitInfo(pt);
            if (info.Column == column) {
                if (info.InColumn) {
                    if (SelectedCount == _view.DataRowCount)
                        ClearSelection();
                    else
                        SelectAll();
                }
                if (info.InRowCell) {
                    InvertRowSelection(info.RowHandle);
                }
            }
            if (info.InRow && _view.IsGroupRow(info.RowHandle) && info.HitTest != GridHitTest.RowGroupButton) {
                InvertRowSelection(info.RowHandle);
            }
        }
        void ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e) {
            ArrayList tempSelection = selection.Clone() as ArrayList;
            var View = _view;
            Detach();
            Attach(View);
            for (int i = 0; i < tempSelection.Count; i++) {
                //(selection[i] as System.Data.DataRowView).Row[CheckMarkColumn]
            }

        }
        //void TableFilled(object sender, DataTableNewRowEventArgs e) {
        //    ArrayList tempSelection = selection.Clone() as ArrayList;
        //    var View = _view;
        //    Detach();
        //    Attach(View);
        //    for (int i = 0; i < tempSelection.Count; i++) {
        //        //(selection[i] as System.Data.DataRowView).Row[CheckMarkColumn]
        //    }
        //}
        //void View_DatasourceChanged(object sender, EventArgs e) {
        //    var ds = _view.DataSource as DataView;
        //    ds.Table.TableNewRow += new DataTableNewRowEventHandler(TableFilled);
        //}
        void View_CustomDrawColumnHeader(object sender, ColumnHeaderCustomDrawEventArgs e) {
            if (e.Column == column) {
                e.Info.InnerElements.Clear();
                e.Painter.DrawObject(e.Info);
                DrawCheckBox(e.Graphics, e.Bounds, SelectedCount == _view.DataRowCount);
                e.Handled = true;
            }
        }
        void View_CustomDrawGroupRow(object sender, RowObjectCustomDrawEventArgs e) {
            DevExpress.XtraGrid.Views.Grid.ViewInfo.GridGroupRowInfo info;
            info = e.Info as DevExpress.XtraGrid.Views.Grid.ViewInfo.GridGroupRowInfo;
            //info.Bounds=new Rectangle( e.Bounds.X,e.Bounds.Y,e.Bounds.Width+30,e.Bounds.Height);
            info.GroupText = info.GroupText.TrimEnd(new char[] { ' ', '.' }) + "     .";
            //var bounds =new Rectangle( e.Bounds.X,e.Bounds.Y,e.Bounds.Width-30,e.Bounds.Height);
            e.Info.Paint.FillRectangle(e.Graphics, e.Appearance.GetBackBrush(e.Cache), e.Bounds);
            e.Painter.DrawObject(e.Info);

            //info.ButtonBounds = new Rectangle(e.Bounds.X,e.Bounds.Y,e.Bounds.Width-30,e.Bounds.Height);

            Rectangle r = info.ButtonBounds;
            r.Offset(e.Bounds.Width - r.Left - r.Width - CheckboxIndent * 2 + 2, 0);
            DrawCheckBox(e.Graphics, r, IsGroupRowSelected(e.RowHandle));
            e.Handled = true;
        }
        void view_RowStyle(object sender, RowStyleEventArgs e) {
            if (IsRowSelected(e.RowHandle)) {
                e.Appearance.BackColor = Color.FromArgb(255, 255, 200);
                e.Appearance.ForeColor = SystemColors.InfoText;
            } else if (_view.IsDataRow(e.RowHandle)) {
                DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo vi = _view.GetViewInfo() as DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo;
                if (e.RowHandle % 2 == 0)
                    e.Appearance.Assign(vi.PaintAppearance.OddRow);
                else
                    e.Appearance.Assign(vi.PaintAppearance.EvenRow);
            }
            e.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far;
        }

        int rowToUnselect = -1;
        void View_SelectionChanged(object sender, DevExpress.Data.SelectionChangedEventArgs e) {
            if (e.ControllerRow != DevExpress.XtraGrid.GridControl.InvalidRowHandle && e.Action == System.ComponentModel.CollectionChangeAction.Add) {
                if (Control.ModifierKeys == Keys.Control) {
                    if (View.FocusedColumn != column)
                        foreach (int row in View.GetSelectedRows()) {
                            if (row == e.ControllerRow) {
                                SelectRow(e.ControllerRow, !IsRowSelected(e.ControllerRow));
                            } else {
                                if (!IsRowSelected(row)) SelectRow(row, true);
                            }

                        }
                    View.ClearSelection();
                }
            } else if (e.ControllerRow != DevExpress.XtraGrid.GridControl.InvalidRowHandle && e.Action == System.ComponentModel.CollectionChangeAction.Remove) {
                //if (Control.ModifierKeys == Keys.Control) SelectRow(e.ControllerRow, false);
            } else if (e.Action == System.ComponentModel.CollectionChangeAction.Refresh && View.SelectedRowsCount > 1) {
                IsBatchAction = true;
                foreach (int row in View.GetSelectedRows()) {
                    if (!IsRowSelected(row) && (row != View.FocusedRowHandle || column != View.FocusedColumn))
                        SelectRow(row, true);

                    if (row != View.FocusedRowHandle)
                        View.UnselectRow(row);
                }
                IsBatchAction = false;
            }
        }

        private void grid_MouseUp(object sender, MouseEventArgs e) {
            if (rowToUnselect != -1) {
                View.UnselectRow(rowToUnselect);
                rowToUnselect = -1;
            }
        }

        private void view_RowCountChanged(object sender, EventArgs e) {
            for (int i = selection.Count - 1; i >= 0; i--) {
                var row = (DataRowView)GetSelectedRow(i);
                if (row.Row.RowState == DataRowState.Detached || row.Row.RowState == DataRowState.Deleted)
                    selection.Remove(row);
            }

            //var ColumnWidths = new Dictionary<GridColumn, int>();
            //int totalWidth = 0, columnWidth = 0;
            //foreach (GridColumn col in _view.VisibleColumns)
            //{
            //    totalWidth += columnWidth = col.GetBestWidth();
            //    ColumnWidths.Add(col, columnWidth);

            //}
            //foreach (GridColumn col in ColumnWidths.Keys)
            //{
            //    col.Width = ColumnWidths[col] * _view.GridControl.Width / totalWidth;
            //}

        }

    }
}
